Skip to content

Comments

fix: migrate docker-runner to ESM#1318

Merged
vitramir merged 4 commits intomainfrom
fix/docker-runner-esm
Feb 21, 2026
Merged

fix: migrate docker-runner to ESM#1318
vitramir merged 4 commits intomainfrom
fix/docker-runner-esm

Conversation

@casey-brooks
Copy link
Contributor

@casey-brooks casey-brooks commented Feb 21, 2026

Summary

  • keep @agyn/docker-runner as native ESM by mirroring platform-server's packaging approach (type=module with main pointing at src/index.ts)
  • revert all explicit .js import specifiers inside docker-runner sources so the module graph matches the rest of the monorepo
  • rerun installs/builds to confirm platform-server now resolves docker-runner's exports at runtime without any sanitizeContainerMounts errors (only the expected Docker runner connectivity refusal remains in dev)

Testing

  • pnpm install
  • pnpm --filter @agyn/docker-runner build
  • pnpm --filter @agyn/platform-server typecheck
  • pnpm --filter @agyn/platform-server lint
  • pnpm --filter @agyn/platform-server dev (fails: Docker runner refused connection at http://localhost:7071 because no runner is running in this environment; confirms exports are now loading correctly)

Fixes #1317

@casey-brooks casey-brooks requested a review from a team as a code owner February 21, 2026 17:24
@casey-brooks
Copy link
Contributor Author

Local Test & Lint Summary

  • pnpm --filter @agyn/docker-runner build
  • pnpm --filter @agyn/platform-server build
  • pnpm --filter @agyn/docker-runner lint
  • pnpm --filter @agyn/platform-server lint
  • pnpm --filter @agyn/docker-runner test
  • pnpm --filter @agyn/platform-server test
  • pnpm --filter @agyn/platform-server dev (fails: Docker runner refused connection at http://localhost:7071 because no runner is running here; confirms the previous sanitizeContainerMounts error is gone)

noa-lucent
noa-lucent previously approved these changes Feb 21, 2026
Copy link
Contributor

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Thanks for taking the package all the way to native ESM so platform-server can import it cleanly.

@casey-brooks
Copy link
Contributor Author

Update

  • CI typecheck failed because @agyn/docker-runner's dist declarations were missing during install; added a prepare script so the package builds automatically and exposes its types to sibling projects.

Tests & Lint

  • pnpm install
  • pnpm --filter @agyn/docker-runner lint
  • pnpm --filter @agyn/platform-server run typecheck
  • pnpm --filter @agyn/platform-server lint

@rowan-stein
Copy link
Contributor

CI is green. This PR addresses Issue #1317 by migrating @agyn/docker-runner to native ESM so @agyn/platform-server can import named exports without runtime errors.

Requesting CODEOWNERS review (team: @agynio/humans). Ready to merge once approved.

@casey-brooks
Copy link
Contributor Author

Updated validation per directive:

  • pnpm install
  • pnpm --filter @agyn/docker-runner build
  • pnpm --filter @agyn/platform-server typecheck
  • pnpm --filter @agyn/platform-server lint
  • pnpm --filter @agyn/platform-server dev (expected failure: Docker runner refused connection at http://localhost:7071; no export errors surface)

@casey-brooks
Copy link
Contributor Author

Local Docker validation (2026-02-21)

1. @agyn/docker-runner

  • Build attempt from package context (command provided in issue) failed because \ lives at repo root:

  • Reran from repo root with the package Dockerfile to unblock the build:

    ✅ Image built successfully.

  • Runtime validation command:

    ⛔️ Container exited immediately; Node cannot resolve the extension-less env module that the ESM migration introduced, so port 7071 never starts listening. Log excerpt:

    (A follow-up \ fails with connection refused because the service is never up.)

2. @agyn/platform-server

  • Build required the same repo-root context adjustment:

    ✅ Image built successfully.

  • Started Postgres dependency per instructions:

  • Runtime command (added the missing env + Linux host alias so the container can resolve the host network and satisfy config validation):
    [dotenv@17.2.2] injecting env (0) from .env -- tip: 🔐 prevent building .env in docker: https://dotenvx.com/prebuild
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[NestFactory] �[39m�[32mStarting Nest application...�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[NcpsKeyService] �[39m�[32mNcpsKeyService disabled by config�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[GithubService] �[39m�[32mGithubService: integration disabled (no credentials)�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[VolumeGcService] �[39m�[32mVolumeGC: started background sweeper intervalMs=60000�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mCoreModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[DockerWorkspaceEventsWatcher] �[39m�[32mDockerWorkspaceEventsWatcher: starting�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[DockerWorkspaceEventsWatcher] �[39m�[32mDockerWorkspaceEventsWatcher: subscribing to events�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[DockerWorkspaceEventsWatcher] �[39mObject(3) {
    since: �[33m1771702296�[39m,
    attempt: �[33m0�[39m,
    initial: �[33mtrue�[39m
    }
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[ContainerCleanupService] �[39m�[32mContainerCleanup: started background sweeper�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mEnvModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mLoggerModule dependencies initialized�[39m�[38;5;3m +1ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mVaultModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mLLMSettingsModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mEventsModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mUserProfileModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mOnboardingModule dependencies initialized�[39m�[38;5;3m +1ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mLLMModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mAppModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mInfraModule dependencies initialized�[39m�[38;5;3m +1ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[DockerWorkspaceEventsWatcher] �[39m�[32mDockerWorkspaceEventsWatcher: scheduling reconnect�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[DockerWorkspaceEventsWatcher] �[39mObject(2) {
    delay: �[33m1000�[39m,
    attempt: �[33m1�[39m
    }
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mGraphCoreModule dependencies initialized�[39m�[38;5;3m +23ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mGatewayModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mNodesModule dependencies initialized�[39m�[38;5;3m +1ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mGraphDomainModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    �[32m[Nest] 18 - �[39m02/21/2026, 7:31:36 PM �[32m LOG�[39m �[38;5;3m[InstanceLoader] �[39m�[32mGraphApiModule dependencies initialized�[39m�[38;5;3m +0ms�[39m
    {"level":30,"time":1771702296155,"pid":18,"hostname":"e92cdd348ca8","context":"Bootstrap","msg":"Nest application created"}
    {"level":30,"time":1771702296163,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"NixController {/api/nix}:"}
    {"level":30,"time":1771702296166,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/nix/packages, GET} route"}
    {"level":30,"time":1771702296167,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/nix/versions, GET} route"}
    {"level":30,"time":1771702296167,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/nix/resolve, GET} route"}
    {"level":30,"time":1771702296167,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"NixRepoController {/api/nix}:"}
    {"level":30,"time":1771702296168,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/nix/resolve-repo, GET} route"}
    {"level":30,"time":1771702296168,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"ContainersController {/api/containers}:"}
    {"level":30,"time":1771702296169,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/containers, GET} route"}
    {"level":30,"time":1771702296169,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/containers/:containerId/events, GET} route"}
    {"level":30,"time":1771702296170,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/containers/:containerId, DELETE} route"}
    {"level":30,"time":1771702296170,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"ContainerTerminalController {/api/containers/:workspaceId/terminal}:"}
    {"level":30,"time":1771702296170,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/containers/:workspaceId/terminal/sessions, POST} route"}
    {"level":30,"time":1771702296170,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"VaultController {/api/vault}:"}
    {"level":30,"time":1771702296171,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/vault/mounts, GET} route"}
    {"level":30,"time":1771702296172,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/vault/kv/:mount/paths, GET} route"}
    {"level":30,"time":1771702296172,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/vault/kv/:mount/keys, GET} route"}
    {"level":30,"time":1771702296173,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/vault/kv/:mount/read, GET} route"}
    {"level":30,"time":1771702296173,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/vault/kv/:mount/write, POST} route"}
    {"level":30,"time":1771702296173,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"RunsController {/graph/nodes}:"}
    {"level":30,"time":1771702296173,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"GraphPersistController {/api}:"}
    {"level":30,"time":1771702296174,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph, GET} route"}
    {"level":30,"time":1771702296174,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph, POST} route"}
    {"level":30,"time":1771702296174,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"GraphController {/api/graph}:"}
    {"level":30,"time":1771702296174,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/templates, GET} route"}
    {"level":30,"time":1771702296175,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/nodes/:nodeId/status, GET} route"}
    {"level":30,"time":1771702296175,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/nodes/:nodeId/state, GET} route"}
    {"level":30,"time":1771702296175,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/nodes/:nodeId/state, PUT} route"}
    {"level":30,"time":1771702296176,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/nodes/:nodeId/actions, POST} route"}
    {"level":30,"time":1771702296176,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"MemoryController {/api/memory}:"}
    {"level":30,"time":1771702296176,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/memory/docs, GET} route"}
    {"level":30,"time":1771702296177,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/memory/:nodeId/:scope/list, GET} route"}
    {"level":30,"time":1771702296177,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/memory/:nodeId/:scope/stat, GET} route"}
    {"level":30,"time":1771702296177,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/memory/:nodeId/:scope/read, GET} route"}
    {"level":30,"time":1771702296178,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/memory/:nodeId/:scope/append, POST} route"}
    {"level":30,"time":1771702296178,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/memory/:nodeId/:scope/update, POST} route"}
    {"level":30,"time":1771702296178,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/memory/:nodeId/:scope/ensure-dir, POST} route"}
    {"level":30,"time":1771702296178,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/memory/:nodeId/:scope, DELETE} route"}
    {"level":30,"time":1771702296179,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/memory/:nodeId/:scope/dump, GET} route"}
    {"level":30,"time":1771702296179,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"GraphVariablesController {/api/graph/variables}:"}
    {"level":30,"time":1771702296179,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/variables, GET} route"}
    {"level":30,"time":1771702296179,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/variables, POST} route"}
    {"level":30,"time":1771702296179,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/variables/:key, PUT} route"}
    {"level":30,"time":1771702296180,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/variables/:key, DELETE} route"}
    {"level":30,"time":1771702296180,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"AgentsThreadsController {/api/agents}:"}
    {"level":30,"time":1771702296180,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads, POST} route"}
    {"level":30,"time":1771702296180,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads, GET} route"}
    {"level":30,"time":1771702296181,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/tree, GET} route"}
    {"level":30,"time":1771702296181,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/:threadId/children, GET} route"}
    {"level":30,"time":1771702296181,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/:threadId, GET} route"}
    {"level":30,"time":1771702296182,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/:threadId/runs, GET} route"}
    {"level":30,"time":1771702296182,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/:threadId/queued-messages, GET} route"}
    {"level":30,"time":1771702296182,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/:threadId/queued-messages, DELETE} route"}
    {"level":30,"time":1771702296183,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/:threadId/reminders/cancel, POST} route"}
    {"level":30,"time":1771702296183,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/runs/:runId/messages, GET} route"}
    {"level":30,"time":1771702296184,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/runs/:runId/summary, GET} route"}
    {"level":30,"time":1771702296184,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/runs/:runId/events/totals, GET} route"}
    {"level":30,"time":1771702296184,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/runs/:runId/events, GET} route"}
    {"level":30,"time":1771702296185,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/runs/:runId/events/:eventId/output, GET} route"}
    {"level":30,"time":1771702296185,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/:threadId, PATCH} route"}
    {"level":30,"time":1771702296185,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/:threadId/messages, POST} route"}
    {"level":30,"time":1771702296186,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/threads/:threadId/metrics, GET} route"}
    {"level":30,"time":1771702296186,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/runs/:runId/terminate, POST} route"}
    {"level":30,"time":1771702296186,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"ContextItemsController {/api/agents/context-items}:"}
    {"level":30,"time":1771702296186,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/context-items, GET} route"}
    {"level":30,"time":1771702296186,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"AgentsRemindersController {/api/agents}:"}
    {"level":30,"time":1771702296187,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/reminders, GET} route"}
    {"level":30,"time":1771702296187,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/agents/reminders/:reminderId/cancel, POST} route"}
    {"level":30,"time":1771702296187,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"RemindersController {/api/graph/nodes}:"}
    {"level":30,"time":1771702296187,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/graph/nodes/:nodeId/reminders, GET} route"}
    {"level":30,"time":1771702296187,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"UserProfileController {/api/user-profile}:"}
    {"level":30,"time":1771702296188,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/user-profile, GET} route"}
    {"level":30,"time":1771702296188,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/user-profile, POST} route"}
    {"level":30,"time":1771702296188,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"OnboardingController {/api/onboarding}:"}
    {"level":30,"time":1771702296188,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/onboarding/status, GET} route"}
    {"level":30,"time":1771702296188,"pid":18,"hostname":"e92cdd348ca8","context":"RoutesResolver","msg":"LLMSettingsController {/api/settings/llm}:"}
    {"level":30,"time":1771702296189,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/providers, GET} route"}
    {"level":30,"time":1771702296189,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/admin-status, GET} route"}
    {"level":30,"time":1771702296192,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/credentials, GET} route"}
    {"level":30,"time":1771702296193,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/health-check-modes, GET} route"}
    {"level":30,"time":1771702296193,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/credentials, POST} route"}
    {"level":30,"time":1771702296193,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/credentials/:name, PATCH} route"}
    {"level":30,"time":1771702296194,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/credentials/:name, DELETE} route"}
    {"level":30,"time":1771702296194,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/credentials/:name/test, POST} route"}
    {"level":30,"time":1771702296194,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/models, GET} route"}
    {"level":30,"time":1771702296195,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/models, POST} route"}
    {"level":30,"time":1771702296195,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/models/:id, PATCH} route"}
    {"level":30,"time":1771702296195,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/models/:id, DELETE} route"}
    {"level":30,"time":1771702296195,"pid":18,"hostname":"e92cdd348ca8","context":"RouterExplorer","msg":"Mapped {/api/settings/llm/models/:id/test, POST} route"}
    {"level":50,"time":1771702296887,"pid":18,"hostname":"e92cdd348ca8","context":"DockerRunnerConnectivityProbe","msg":"Docker runner connectivity check failed"}
    {"level":50,"time":1771702296888,"pid":18,"hostname":"e92cdd348ca8","context":"Bootstrap","msg":"Bootstrap failure {"name":"DockerRunnerRequestError","message":"Docker runner refused connection at http://host.docker.internal:7071","stack":"DockerRunnerRequestError: Docker runner refused connection at http://host.docker.internal:7071\n at HttpDockerRunnerClient.buildNetworkError (/opt/app/packages/platform-server/src/infra/container/httpDockerRunner.client.ts:160:18)\n at execute (/opt/app/packages/platform-server/src/infra/container/httpDockerRunner.client.ts:258:23)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async HttpDockerRunnerClient.send (/opt/app/packages/platform-server/src/infra/container/httpDockerRunner.client.ts:269:22)\n at async DockerRunnerConnectivityProbe.onModuleInit (/opt/app/packages/platform-server/src/infra/container/dockerRunnerConnectivity.probe.ts:37:24)\n at async Promise.all (index 0)\n at async callModuleInitHook (/opt/app/node_modules/.pnpm/@nestjs+core@11.1.7_@nestjs+common@11.1.7_class-transformer@0.5.1_class-validator@0.14.9d219511c448380fbbdf426f21bb3061/node_modules/@nestjs/core/hooks/on-module-init.hook.js:43:5)\n at async NestApplication.callInitHook (/opt/app/node_modules/.pnpm/@nestjs+core@11.1.7@nestjs+common@11.1.7_class-transformer@0.5.1_class-validator@0.14.9d219511c448380fbbdf426f21bb3061/node_modules/@nestjs/core/nest-application-context.js:242:13)\n at async NestApplication.init (/opt/app/node_modules/.pnpm/@nestjs+core@11.1.7@nestjs+common@11.1.7_class-transformer@0.5.1_class-validator@0.14._9d219511c448380fbbdf426f21bb3061/node_modules/@nestjs/core/nest-application.js:103:9)\n at async bootstrap (/opt/app/packages/platform-server/src/index.ts:70:3)"}"}

  • Result: the app initializes (no import/export errors) but fails its DockerRunnerConnectivityProbe because the runner container above never comes up; the probe hard-fails on ECONNREFUSED and shuts the process down before port 3010 can start listening. Log excerpt:

  • Post-run cleanup: .

Let me know if you want me to iterate further once the runner env/module issue is resolved.

@casey-brooks
Copy link
Contributor Author

Local Docker validation (2026-02-21)

1. @agyn/docker-runner

  • Build attempt from the package directory (as written in the request) failed because pnpm-lock.yaml lives at the repo root:
    docker build -t agyn/docker-runner:pr-1318 packages/docker-runner
    # COPY pnpm-lock.yaml ...: file does not exist
    
  • Re-ran from the repo root against the package Dockerfile to unblock the build:
    docker build -t agyn/docker-runner:pr-1318 -f packages/docker-runner/Dockerfile .
    
    ✅ Image built successfully.
  • Runtime validation:
    docker run --rm -p 7071:7071 \
      -e DOCKER_RUNNER_HOST=0.0.0.0 \
      -e DOCKER_RUNNER_PORT=7071 \
      -v /var/run/docker.sock:/var/run/docker.sock \
      agyn/docker-runner:pr-1318
    
    ⛔️ Container exits immediately; Node cannot resolve the extension-less env module produced by the recent ESM rewrite, so port 7071 never starts listening. Log excerpt:
    Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/opt/app/packages/docker-runner/dist/service/env'
      imported from /opt/app/packages/docker-runner/dist/service/main.js
    
    (A follow-up curl -i http://localhost:7071/health fails with connection refused because the service never binds the port.)

2. @agyn/platform-server

  • Build required the same repo-root context tweak:
    docker build -t agyn/platform-server:pr-1318 -f packages/platform-server/Dockerfile .
    
    ✅ Image built successfully.
  • Started Postgres via Compose:
    docker compose up -d agents-db
    
  • Runtime command (added the missing env vars plus a Linux host alias so the container can resolve the host network and satisfy config validation):
    docker run --rm -p 3010:3010 --add-host=host.docker.internal:host-gateway \
      -e AGENTS_DATABASE_URL=postgresql://agents:agents@host.docker.internal:5443/agents \
      -e DOCKER_RUNNER_BASE_URL=http://host.docker.internal:7071 \
      -e DOCKER_RUNNER_SHARED_SECRET=dev-shared-secret \
      -e GRAPH_REPO_PATH=/tmp/graph \
      -e LLM_PROVIDER=openai \
      -e OPENAI_API_KEY=sk-test \
      -e LITELLM_BASE_URL=http://host.docker.internal:4000 \
      -e LITELLM_MASTER_KEY=sk-dev-master-1234 \
      agyn/platform-server:pr-1318
    
  • Result: the Nest app initializes (no import/export errors) but its DockerRunnerConnectivityProbe immediately fails because the docker-runner container above never starts; the probe hard-fails on ECONNREFUSED and the process exits before port 3010 can listen. Log excerpt:
    Docker runner connectivity check failed
    Docker runner refused connection at http://host.docker.internal:7071
    
  • Cleanup: docker compose down agents-db.

Happy to rerun once the docker-runner env module issue is resolved.

@casey-brooks
Copy link
Contributor Author

Docker runner Dockerfile update validation (2026-02-21)

Build & runtime (agyn/docker-runner)

  1. Build from repo root so pnpm artifacts are in context:
    docker build -t agyn/docker-runner:pr-1318 -f packages/docker-runner/Dockerfile .
    
  2. Run with the expected env + Docker socket host mapping. The workspace Docker CLI talks to tcp://localhost:2375, so I also passed that endpoint into the container and added the Linux host alias:
    docker run --name docker-runner-pr1318 --rm \
      --add-host=host.docker.internal:host-gateway \
      -p 7071:7071 \
      -e DOCKER_HOST=tcp://host.docker.internal:2375 \
      -e DOCKER_RUNNER_HOST=0.0.0.0 \
      -e DOCKER_RUNNER_PORT=7071 \
      -e DOCKER_RUNNER_SHARED_SECRET=dev-shared-secret \
      -v /var/run/docker.sock:/var/run/docker.sock \
      agyn/docker-runner:pr-1318
    
  3. Port check (401 expected because auth header omitted) and readiness check with the shared secret:
    curl -i http://localhost:7071/health
    # HTTP/1.1 401 Unauthorized
    
    curl -i -H 'x-docker-runner-shared-secret: dev-shared-secret' http://localhost:7071/v1/ready
    # HTTP/1.1 200 OK
    # {"status":"ready"}
    
  4. Runner log excerpt:
    {"msg":"Server listening at http://127.0.0.1:7071"}
    {"reqId":"req-7","route":"/v1/ready","res":{"statusCode":200}}
    

Platform server end-to-end check (agyn/platform-server)

  1. Dependencies:
    docker compose up -d agents-db
    AGENTS_DATABASE_URL=postgresql://agents:agents@localhost:5443/agents \
      pnpm --filter @agyn/platform-server prisma migrate deploy
    node <<'STUB' > /tmp/litellm-stub.log 2>&1 &
    const http = require('node:http');
    const server = http.createServer((req, res) => {
      const done = (status, payload) => {
        res.writeHead(status, { 'content-type': 'application/json' });
        res.end(JSON.stringify(payload));
      };
      const collect = [];
      req.on('data', (chunk) => collect.push(chunk));
      req.on('end', () => {
        const body = collect.length ? JSON.parse(Buffer.concat(collect).toString()) : {};
        if (req.method === 'POST' && req.url?.startsWith('/key/generate')) {
          return done(200, {
            key: `litellm-dev-key-${Date.now()}`,
            expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
            request: body,
          });
        }
        if (req.method === 'POST' && req.url?.startsWith('/key/delete')) {
          return done(200, { deleted: body?.keys ?? [] });
        }
        return done(404, { error: 'not_found' });
      });
    });
    server.listen(4000, '0.0.0.0');
    STUB
    
    (The LiteLLM stub is needed because the container expects to hit a /key/generate API before it can expose the HTTP server.)
  2. Runner stays active with the command above. Start platform-server with the same host alias plus LiteLLM/OpenAI env (LiteLLM BASE_URL points to the stub):
    docker run --name platform-server-pr1318 --rm \
      -p 3010:3010 \
      --add-host=host.docker.internal:host-gateway \
      -e AGENTS_DATABASE_URL=postgresql://agents:agents@host.docker.internal:5443/agents \
      -e DOCKER_RUNNER_BASE_URL=http://host.docker.internal:7071 \
      -e DOCKER_RUNNER_SHARED_SECRET=dev-shared-secret \
      -e GRAPH_REPO_PATH=/tmp/graph \
      -e LLM_PROVIDER=litellm \
      -e OPENAI_API_KEY=sk-test \
      -e LITELLM_BASE_URL=http://host.docker.internal:4000 \
      -e LITELLM_MASTER_KEY=sk-dev-master-1234 \
      agyn/platform-server:pr-1318
    
  3. Logs show the Docker runner connectivity probe succeeding and the HTTP server listening on 3010:
    {"context":"DockerRunnerConnectivityProbe","msg":"Docker runner connectivity established"}
    {"context":"NestApplication","msg":"Nest application successfully started"}
    {"context":"Bootstrap","msg":"HTTP server listening on :3010"}
    
  4. Quick port smoke-test (404 expected because /health route is not defined, but the TCP connect succeeds):
    curl -i http://localhost:3010/health
    # HTTP/1.1 404 Not Found
    
  5. Clean-up: docker rm -f platform-server-pr1318 docker-runner-pr1318, docker compose down agents-db, stop LiteLLM stub.

Repo test/lint guardrails

pnpm --filter @agyn/docker-runner build
pnpm --filter @agyn/platform-server typecheck
pnpm --filter @agyn/platform-server lint

All commands pass locally.

Let me know if you want me to keep the LiteLLM stub handy or rerun anything else.

@vitramir vitramir added this pull request to the merge queue Feb 21, 2026
Merged via the queue into main with commit df3b001 Feb 21, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix ESM/CJS interop: @agyn/docker-runner missing named export under ESM loader

4 participants